Skip to content

Add unit tests for analytics, exercise, routine, target managers, and… - #45

Merged
Devasy merged 4 commits into
mainfrom
adds-tests
May 4, 2026
Merged

Add unit tests for analytics, exercise, routine, target managers, and…#45
Devasy merged 4 commits into
mainfrom
adds-tests

Conversation

@Devasy

@Devasy Devasy commented May 1, 2026

Copy link
Copy Markdown
Owner

… target calculators

  • Created analytics_manager_test.dart to test session indexing, growth model updates, recommendations, and volume progression.
  • Added exercise_manager_test.dart for loading exercises, adding custom exercises, and filtering/searching functionalities.
  • Implemented routine_manager_test.dart to validate routine loading, creation, updating, and deletion.
  • Developed target_calculator_test.dart to ensure correct calculations for reps, weight, and volume targets.
  • Introduced target_manager_test.dart to test target creation, updates, deletions, and retrievals for exercises.

Summary by CodeRabbit

  • New Features

    • Added Health Connect sync button in history to sync all unsynced workouts at once.
  • Bug Fixes

    • Improved Health Connect segment handling to avoid zero-duration/overlap issues and make syncing more reliable.
  • Tests

    • Added comprehensive tests covering workout flow, analytics, exercises, routines, targets, and related utilities.

… target calculators

- Created `analytics_manager_test.dart` to test session indexing, growth model updates, recommendations, and volume progression.
- Added `exercise_manager_test.dart` for loading exercises, adding custom exercises, and filtering/searching functionalities.
- Implemented `routine_manager_test.dart` to validate routine loading, creation, updating, and deletion.
- Developed `target_calculator_test.dart` to ensure correct calculations for reps, weight, and volume targets.
- Introduced `target_manager_test.dart` to test target creation, updates, deletions, and retrievals for exercises.
@coderabbitai

coderabbitai Bot commented May 1, 2026

Copy link
Copy Markdown
Contributor

Warning

Rate limit exceeded

@Devasy has exceeded the limit for the number of commits that can be reviewed per hour. Please wait 47 minutes and 41 seconds before requesting another review.

To keep reviews running without waiting, you can enable usage-based add-on for your organization. This allows additional reviews beyond the hourly cap. Account admins can enable it under billing.

⌛ How to resolve this issue?

After the wait time has elapsed, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

We recommend that you space out your commits to avoid hitting the rate limit.

🚦 How do rate limits work?

CodeRabbit enforces hourly rate limits for each developer per organization.

Our paid plans have higher rate limits than the trial, open-source and free plans. In all cases, we re-allow further reviews after a brief timeout.

Please see our FAQ for further information.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: ASSERTIVE

Plan: Pro

Run ID: 5da3ecab-c4e1-4a1a-bce8-081f7588cdb7

📥 Commits

Reviewing files that changed from the base of the PR and between 84b6045 and 9d2bd97.

📒 Files selected for processing (1)
  • workout-logger/test/target_calculator_test.dart

Walkthrough

Adds a Health Connect bulk-sync UI and a manager API to trigger syncing of all unsynced sessions, tightens segment-building fallback logic in HealthConnectService to avoid zero-duration final segments, and adds comprehensive unit tests for multiple managers and calculator strategies.

Changes

Health Connect Sync + Segment Robustness

Layer / File(s) Summary
Manager API
workout-logger/lib/services/managers/history_manager.dart
Adds void syncAllUnsynced() that iterates in-memory sessions and, when an IHealthSyncManager exists, calls syncSession for sessions with hcSyncedAt == null using _onHcSynced for persistence.
UI Integration
workout-logger/lib/screens/history_screen.dart
HistoryScreen now watches SettingsProvider, computes hasUnsynced when Health Connect is enabled and any session has hcSyncedAt == null, and shows an AppBar sync action that calls historyManager.syncAllUnsynced() and displays a “Syncing all unsynced workouts…” snackbar.
Robustness
workout-logger/lib/services/health_connect_service.dart
_buildSegments fallback criteria expanded: previously fell back on non-unique clamped timestamps; now also falls back when the final clamped timestamp equals sessionEnd to avoid zero-duration final segments.

Manager / Calculator Test Suites

Layer / File(s) Summary
ActiveWorkoutManager Tests
workout-logger/test/active_workout_manager_test.dart
Adds tests for start/cancel/finish workflows, set operations, notes, navigation, persistence, and routine-start behavior.
ExerciseManager Tests
workout-logger/test/exercise_manager_test.dart
Adds tests for loading built-in/custom exercises, getExercise/getExerciseName, add/delete custom exercise behavior and validation, and search/filter helpers.
RoutineManager Tests
workout-logger/test/routine_manager_test.dart
Adds tests for loading, creating (unique IDs), retrieving, updating, deleting routines, and exercise-membership queries.
TargetCalculator Tests
workout-logger/test/target_calculator_test.dart
Adds tests for reps/weight/volume calculators, factory lookup (case-insensitive), dynamic registration, delegation, and reset behavior.
AnalyticsManager Tests
workout-logger/test/analytics_manager_test.dart
Adds tests for session indexing, growth-model training/clearing and callbacks, recommendations (ML/default/fallback), volume progression, weekly volume-by-muscle, and quick stats.
TargetManager Tests
workout-logger/test/target_manager_test.dart
Adds tests for target creation across types, current-value and completion logic, ML-driven estimated completion, persistence, recalculation, partitioning, deletion, and exercise-target queries.

Possibly related PRs

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The PR title references adding unit tests for analytics, exercise, routine, target managers and calculators, which directly matches the main objective of introducing comprehensive test coverage.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

Tip

💬 Introducing [Slack Agent](https://www.coderabbit.ai/agent): Turn conversations into code.

Slack Agent is built on CodeRabbit's deep understanding of your code, so your team can collaborate across the entire SDLC without losing context.

  • Generate code and open pull requests
  • Plan features and break down work
  • Investigate incidents and troubleshoot customer tickets together
  • Automate recurring tasks and respond to alerts with triggers
  • Summarize progress and report instantly

Built for teams:

  • Shared memory across your entire org—no repeating context
  • Per-thread sandboxes to safely plan and execute work
  • Governance built-in—scoped access, auditability, and budget controls

One agent for your entire SDLC. Right inside Slack.

👉 Get your free trial and get 200 agent minutes per Slack user (a $50 value).


Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share
Review rate limit: 0/1 reviews remaining, refill in 47 minutes and 41 seconds.

Comment @coderabbitai help to get the list of available commands and usage tips.

@codecov

codecov Bot commented May 1, 2026

Copy link
Copy Markdown

Codecov Report

❌ Patch coverage is 15.78947% with 16 lines in your changes missing coverage. Please review.
✅ Project coverage is 32.18%. Comparing base (2dd9145) to head (9d2bd97).
⚠️ Report is 2 commits behind head on main.

Files with missing lines Patch % Lines
workout-logger/lib/screens/history_screen.dart 0.00% 9 Missing ⚠️
...-logger/lib/services/managers/history_manager.dart 0.00% 5 Missing ⚠️
...ut-logger/lib/services/health_connect_service.dart 0.00% 2 Missing ⚠️
Additional details and impacted files
@@            Coverage Diff             @@
##             main      #45      +/-   ##
==========================================
+ Coverage   29.13%   32.18%   +3.04%     
==========================================
  Files          33       36       +3     
  Lines        5999     6187     +188     
==========================================
+ Hits         1748     1991     +243     
+ Misses       4251     4196      -55     

☔ View full report in Codecov by Sentry.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 7

🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@workout-logger/test/analytics_manager_test.dart`:
- Around line 7-13: Change the helper builders _session and _exercise to use
only named parameters (make id, exerciseId, muscleGroupId, and other args named)
because they have 3+ arguments; update every call site that uses positional
invocation (e.g. _session('s1','ex1',...) and _exercise('ex1','chest')) to use
named invocation (e.g. _session(id: 's1', exerciseId: 'ex1', ...), _exercise(id:
'ex1', muscleGroupId: 'chest')), ensuring default values and required
annotations are preserved (e.g. keep required DateTime date) and adjust any
tests referencing those helpers accordingly.
- Around line 348-357: The test uses DateTime.now() which makes getQuickStats
non-deterministic; change the session timestamp passed to
mockStorage.addMockSession/_session (the call with _session('s1', 'ex1', date:
DateTime.now())) to a fixed, deterministic DateTime (e.g., a hard-coded UTC
constant) so the test always uses the same date when manager.getQuickStats() is
evaluated.
- Around line 141-177: The test "trains a model for every exercise present in
sessions" only asserts that a model for 'ex1' was created; update the test to
also assert that manager.getGrowthModel('ex2') is not null after calling
manager.trainAllGrowthModels(sessions), so both exerciseIds present in the
sessions (ex1 and ex2) are verified as having models trained; reference the
existing sessions array and the manager.trainAllGrowthModels(...) invocation to
add the additional expectation.

In `@workout-logger/test/exercise_manager_test.dart`:
- Around line 127-147: Update the two tests ('throws ArgumentError for empty
name' and 'throws ArgumentError for invalid category') to use await expectLater
instead of expect so the async matcher is awaited and lint/unnecessary_async is
satisfied: call await expectLater(manager.addCustomExercise(name: ..., category:
..., primaryMuscleGroupId: ...), throwsArgumentError) for each case (referencing
manager.addCustomExercise) so the Future is awaited and failures report
correctly.

In `@workout-logger/test/routine_manager_test.dart`:
- Around line 93-99: The test adds an orphan routine via manager.updateRoutine
but only asserts in-memory state; add an assertion that the routine was
persisted to storage by verifying the fake/mock storage contains the routine or
that the storage save method was called (reference the manager.updateRoutine
call and the internal _storage.saveRoutine behavior) — e.g., after awaiting
manager.updateRoutine(orphan) assert the storage mock recorded a save for id
'orphan' or that storage.getRoutine('orphan') returns the saved Routine,
ensuring persistence is exercised as well as manager.routines.

In `@workout-logger/test/target_calculator_test.dart`:
- Around line 54-59: The test points out that WeightTargetCalculator.calculate
(in target_calculator.dart) can throw a StateError because it calls .reduce() on
the mapped sets when a matching ExerciseLog has an empty sets list; update the
calculate implementation to guard for empty iterables by replacing the unsafe
.reduce(...) with .fold(initialValue, ...) or by checking for empty sets before
reducing so an empty ExerciseLog yields 0, and add the suggested test case that
builds a WorkoutSession with ExerciseLog(exerciseId: 'ex1', sets: []) to assert
calculator.calculate('ex1', sessions) returns 0 to prevent regressions.
- Around line 162-176: The test helper class _AlwaysReturns42 has no instance
fields so add a const constructor (e.g., const _AlwaysReturns42();) to the class
and replace its non-constant instantiations with const instances; update the
TargetCalculatorFactory.registerCalculator calls that currently use
_AlwaysReturns42() (for example the 'custom' and 'extra' registrations and any
other test sites) to use const _AlwaysReturns42() so the tests follow the "use
const constructors wherever possible" guideline.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: ASSERTIVE

Plan: Pro

Run ID: 79883ad2-1c9b-4a26-ac1c-82ac955e4d86

📥 Commits

Reviewing files that changed from the base of the PR and between aadc311 and 8aa3bb9.

📒 Files selected for processing (9)
  • workout-logger/lib/screens/history_screen.dart
  • workout-logger/lib/services/health_connect_service.dart
  • workout-logger/lib/services/managers/history_manager.dart
  • workout-logger/test/active_workout_manager_test.dart
  • workout-logger/test/analytics_manager_test.dart
  • workout-logger/test/exercise_manager_test.dart
  • workout-logger/test/routine_manager_test.dart
  • workout-logger/test/target_calculator_test.dart
  • workout-logger/test/target_manager_test.dart

Comment thread workout-logger/test/analytics_manager_test.dart Outdated
Comment thread workout-logger/test/analytics_manager_test.dart
Comment thread workout-logger/test/analytics_manager_test.dart
Comment thread workout-logger/test/exercise_manager_test.dart
Comment thread workout-logger/test/routine_manager_test.dart
Comment on lines +54 to +59
group('WeightTargetCalculator', () {
final calculator = WeightTargetCalculator();

test('returns 0 when no sessions', () {
expect(calculator.calculate('ex1', []), 0);
});

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor | ⚡ Quick win

Missing edge case: WeightTargetCalculator crashes on a matched log with empty sets

WeightTargetCalculator.calculate() calls .reduce() on log.sets.map(…) (see target_calculator.dart lines 60–62). Dart's own docs note "Consider using fold if the iterable can be empty" — reduce throws StateError when the iterable is empty. The existing "returns 0 when no sessions" test passes an empty sessions list, which bypasses the inner reduce entirely. A session that matches the exercise ID but contains zero sets will crash the production calculator. Adding a test here would surface this latent bug.

➕ Suggested test to add
test('returns 0 when matched exercise log has empty sets', () {
  final sessions = [
    WorkoutSession(
      id: 's1',
      date: DateTime(2024, 1, 1),
      duration: 30,
      exercises: [ExerciseLog(exerciseId: 'ex1', sets: [])],
    ),
  ];
  // Currently throws StateError — exposes the unguarded reduce() in WeightTargetCalculator
  expect(calculator.calculate('ex1', sessions), 0);
});
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@workout-logger/test/target_calculator_test.dart` around lines 54 - 59, The
test points out that WeightTargetCalculator.calculate (in
target_calculator.dart) can throw a StateError because it calls .reduce() on the
mapped sets when a matching ExerciseLog has an empty sets list; update the
calculate implementation to guard for empty iterables by replacing the unsafe
.reduce(...) with .fold(initialValue, ...) or by checking for empty sets before
reducing so an empty ExerciseLog yields 0, and add the suggested test case that
builds a WorkoutSession with ExerciseLog(exerciseId: 'ex1', sets: []) to assert
calculator.calculate('ex1', sessions) returns 0 to prevent regressions.

Comment thread workout-logger/test/target_calculator_test.dart Outdated

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
workout-logger/lib/services/strategies/target_calculator.dart (1)

36-82: 🧹 Nitpick | 🔵 Trivial | 💤 Low value

Add const constructors to all three calculator classes.

None of RepsTargetCalculator, WeightTargetCalculator, or VolumeTargetCalculator declare an explicit constructor, so they cannot be instantiated with const — both the factory map and the test-file calculator declarations are therefore non-const. Adding trivial const constructors fixes this across both files.

♻️ Proposed fix
 class RepsTargetCalculator implements TargetCalculatorStrategy {
+  const RepsTargetCalculator();
   `@override`
   double calculate(String exerciseId, List<WorkoutSession> sessions) {
 class WeightTargetCalculator implements TargetCalculatorStrategy {
+  const WeightTargetCalculator();
   `@override`
   double calculate(String exerciseId, List<WorkoutSession> sessions) {
 class VolumeTargetCalculator implements TargetCalculatorStrategy {
+  const VolumeTargetCalculator();
   `@override`
   double calculate(String exerciseId, List<WorkoutSession> sessions) {

Then in the factory:

   static final Map<String, TargetCalculatorStrategy> _strategies = {
-    'reps': RepsTargetCalculator(),
-    'weight': WeightTargetCalculator(),
-    'volume': VolumeTargetCalculator(),
+    'reps': const RepsTargetCalculator(),
+    'weight': const WeightTargetCalculator(),
+    'volume': const VolumeTargetCalculator(),
   };

As per coding guidelines: "Use const constructors wherever possible."

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@workout-logger/lib/services/strategies/target_calculator.dart` around lines
36 - 82, The three strategy classes (RepsTargetCalculator,
WeightTargetCalculator, VolumeTargetCalculator) lack explicit constructors so
they cannot be instantiated as const; add trivial const constructors to each
class (e.g. const RepsTargetCalculator(); const WeightTargetCalculator(); const
VolumeTargetCalculator();) so callers and factory maps can use const instances
and follow the "use const constructors wherever possible" guideline.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@workout-logger/test/target_calculator_test.dart`:
- Around line 92-114: The VolumeTargetCalculator tests are missing coverage that
ensures sessions for other exercises are ignored; add a test in the
VolumeTargetCalculator group that creates sessions for 'ex1' and a different
exercise (e.g., 'ex2') and asserts calculator.calculate('ex1', sessions) only
counts volume from 'ex1' (use the same _session helper and WorkoutSet entries as
in other tests). This verifies the shared filter (e.g.,
_getExerciseLogsForExercise) is applied for VolumeTargetCalculator.calculate and
prevents regressions.

---

Outside diff comments:
In `@workout-logger/lib/services/strategies/target_calculator.dart`:
- Around line 36-82: The three strategy classes (RepsTargetCalculator,
WeightTargetCalculator, VolumeTargetCalculator) lack explicit constructors so
they cannot be instantiated as const; add trivial const constructors to each
class (e.g. const RepsTargetCalculator(); const WeightTargetCalculator(); const
VolumeTargetCalculator();) so callers and factory maps can use const instances
and follow the "use const constructors wherever possible" guideline.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: ASSERTIVE

Plan: Pro

Run ID: c9908042-020b-40b9-86fe-3709a38a520b

📥 Commits

Reviewing files that changed from the base of the PR and between 8aa3bb9 and 84b6045.

📒 Files selected for processing (5)
  • workout-logger/lib/services/strategies/target_calculator.dart
  • workout-logger/test/analytics_manager_test.dart
  • workout-logger/test/exercise_manager_test.dart
  • workout-logger/test/routine_manager_test.dart
  • workout-logger/test/target_calculator_test.dart

Comment thread workout-logger/test/target_calculator_test.dart
@Devasy

Devasy commented May 4, 2026

Copy link
Copy Markdown
Owner Author

@coderabbitai review

@coderabbitai

coderabbitai Bot commented May 4, 2026

Copy link
Copy Markdown
Contributor
✅ Actions performed

Review triggered.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

@Devasy
Devasy merged commit 7713723 into main May 4, 2026
3 of 4 checks passed
@Devasy
Devasy deleted the adds-tests branch May 4, 2026 17:02
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant